package com.example.win.android_banduyujia.http;

import android.net.Uri;
import android.util.Log;

import com.example.win.android_banduyujia.BanDuApp;
import com.example.win.android_banduyujia.lib.util.StrUtils;
import com.example.win.android_banduyujia.login.application.LoginApplication;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * Created by Win on 2015/8/24.
 */
public class HttpUtils {

    public static final String  LOG_TAG = HttpUtils.class.getSimpleName();

    public static int CONNECTION_TIMEOUT = 20000;
    public static int READ_TIMEOUT = 60000;
    private static final String BOUNDARY = "c9152e99a2d6487fb0bfd02adec3aa16";
    public static final String  MP_BOUNDARY= "--" + BOUNDARY;
    public static final String  END_MP_BOUNDARY= "--" + BOUNDARY + "--";
    public static final String  MULTIPART_FORM_DATA= "multipart/form-data";


    public static JSONObject uploadImage(String url, Map<String, String> header,
                                   Map<String, String> params, String fileKey, String fileName, String file) {
        ByteArrayOutputStream  bos = null;
        HttpClient  client = null;

        try {
            Log.v(LOG_TAG, "file start url" + url);

            client = HttpUtils.getHttpClient();
            HttpPost post = new HttpPost(url);
            addHttpPostHeader(post, header);
            post.setHeader("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);
            post.setHeader("Connection", "keep-alive");
            bos = new ByteArrayOutputStream();
            paramToUpload(bos, params);
            bos.write(("\r\n" + END_MP_BOUNDARY + "\r\n").getBytes("utf-8"));
            imageContentToUpload(bos, fileKey, fileName, file);
            byte[]  data = bos.toByteArray();
            ByteArrayEntity  formEntity = new ByteArrayEntity(data);
            post.setEntity(formEntity);
            HttpResponse  response = client.execute(post);
            StatusLine  status = response.getStatusLine();
            int  statusCode = status.getStatusCode();

            Log.v("postcode", "code="+statusCode);

            if(statusCode == 200){
                String result = EntityUtils.toString(response.getEntity());
                JSONObject  json = new JSONObject(result);
                return json;
            }
            return null;
        } catch (JSONException e) {
                e.printStackTrace();
            Log.v(LOG_TAG, "uploadImage.1: " + e.toString());
        }catch (Exception e){
            Log.v(LOG_TAG, "uploadImage.2: " + e.toString());
        }finally {
            if(bos !=null){
                try{
                     bos.close();
                }catch (IOException e){
                    Log.v(LOG_TAG, "uploadImage.3: " + e.toString());
                }
            }
            if(client !=null){
                client.getConnectionManager().shutdown();
            }
        }
        return null;
    }

    private static void paramToUpload(OutputStream bos, Map<String, String> params) {
        Set<String>  keys = params.keySet();
        for(Iterator<String> it = keys.iterator(); it.hasNext();){
            String key = it.next();
            StringBuilder temp = new StringBuilder(10);
            temp.setLength(0);
            temp.append(MP_BOUNDARY).append("\r\n");
            temp.append("content-disposition: form-data; name=\"").append(key).append("\"\r\n\r\n");
            temp.append(params.get(key)).append("\r\n");
            Log.v(LOG_TAG,"key="+key+"&value="+params.get(key));
            try{
                byte[] res = temp.toString().getBytes("utf-8");
                bos.write(res);
            }catch (IOException e){
                Log.v(LOG_TAG, "paramToUpload: " + e.toString());
            }
        }
    }

    private static void imageContentToUpload(OutputStream out, String fileKey, String fileName, String imgpath) {
        if(StrUtils.isEmpty(imgpath)){
            return;
        }
        StringBuilder temp = new StringBuilder();
        temp.append(MP_BOUNDARY).append("\r\n");
        temp.append("Content-Disposition: form-data;name=\""+fileKey+"\";filename=\"").append(fileName).append("\"\r\n");
        String  filetype = "image/jpeg";
        temp.append("Content-Type:").append(filetype).append("\r\n\r\n");
        FileInputStream fis = null;
        try{
           byte[] res = temp.toString().getBytes("utf-8");
           out.write(res);
           fis = new FileInputStream(imgpath);
           byte[]  buffer = new byte[1024];
            int count = 0;
            while ((count = fis.read(buffer, 0, 1024))>0){
                out.write(buffer, 0, count);
            }
            out.write("\r\n".getBytes("utf-8"));
            out.write(("\r\n"+ END_MP_BOUNDARY).getBytes("utf-8"));
        }catch (IOException e){
            Log.v(LOG_TAG, "imageContentToUpload.1: " + e.toString());
        }finally {
            if(null != fis){
                try{
                    fis.close();
                }catch (IOException e){
                    Log.v(LOG_TAG, "imageContentToUpload.2: " + e.toString());
                }
            }
        }
    }

    public static HttpClient getHttpClient() throws NoSuchAlgorithmException,
            CertificateException, IOException, KeyStoreException,
            KeyManagementException, UnrecoverableKeyException {
        HttpParams params = new BasicHttpParams();

        HttpConnectionParams.setStaleCheckingEnabled(params, false);

        HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, READ_TIMEOUT);
        HttpConnectionParams.setSocketBufferSize(params, 8192);
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);
        SSLSocketFactory factory = new CustomSSLSocketFactory(trustStore);
        schemeRegistry.register(new Scheme("https", factory, 443));

        ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
        return new DefaultHttpClient(manager, params);
    }

    public static HttpResult doGet(String requestURL,
                                   Map<String, String> header, Map<String, String> entity) {
        showDebug("doGet START");

        showDebug("URL: " + requestURL);
        HttpResult httpResult = new HttpResult();
        httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_FAILED);
        if (StrUtils.isEmpty(requestURL)) {
            return httpResult;
        }
        if (!NetworkUtils.isNetWorkAvailable(BanDuApp.getContext())) {
            httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_NET_OFF);
            httpResult.setErrorMessage("Net work is unavailable.");
            return httpResult;
        }
        GZIPInputStream responseGZipDataInputStream = null;
        InputStream in = null;
        HttpClient client = null;

        try {
            String url = toHttpGetUrl(requestURL, entity);
            showDebug("Final URL: " + url);
            HttpGet httpGet = new HttpGet(url);
            addHttpGetHeader(httpGet, header);
            client = HttpUtils.getHttpClient();
            HttpResponse response = client.execute(httpGet);

            showDebug("getting HTTP Status " + url);

            int status = response.getStatusLine().getStatusCode();
            httpResult.setIsConnected(true);
            httpResult.setHttpStatus(status);

            showDebug("HTTP Status: " + status);

            //登陆错误重新登陆~
            if(status == HttpURLConnection.HTTP_UNAUTHORIZED){
                //退出
                showDebug("HTTP Status: " + "get again");
                entity.remove("access_token");
                LoginApplication.getInstance().logout();

                return doGet(requestURL, header, entity);
            }

            if (status == HttpURLConnection.HTTP_OK) {

                String result = null;
                httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_SUCCESS);
                if (response.getEntity().getContentEncoding() != null
                        && "gzip".equalsIgnoreCase(response.getEntity()
                        .getContentEncoding().getValue())) {
                    // Decompression input stream.u
                    in = response.getEntity().getContent();

                    responseGZipDataInputStream = new GZIPInputStream(in);

                    byte[] respBuffer = new byte[1024];
                    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                    int count = -1;
                    while ((count = responseGZipDataInputStream.read(
                            respBuffer, 0, 1024)) != -1) {
                        outStream.write(respBuffer, 0, count);
                    }
                    byte[] data = outStream.toByteArray();
                    outStream.close();
                    result = new String(data);
                } else {
                    result = EntityUtils.toString(response.getEntity());
                }
                showDebug("Result: " + result);
                showDebug("HTTP success");
                httpResult.setData(result);
            } else {
                showDebug("wrong status, failed");
            }
        } catch (IOException e) {
            httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_IO_EXCEPTION);
            String error = StrUtils.notNullStr(e.getMessage());
            httpResult.setErrorMessage(error);
            showDebug("EXC " + error);
        } catch (Exception e) {
            httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_EXCEPTION);
            String error = StrUtils.notNullStr(e.getMessage());
            httpResult.setErrorMessage(error);
            showDebug("EXC " + error);
        } finally {
            try {
                if (responseGZipDataInputStream != null) {
                    responseGZipDataInputStream.close();
                }
            } catch (Exception e) {
                showDebug("is exc " + StrUtils.notNullStr(e.getMessage()));
            }

            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                showDebug("is exc " + StrUtils.notNullStr(e.getMessage()));
            }
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
        }
        showDebug("doGet END");
        return httpResult;
    }

    public static HttpResult doPost(String requestURL, Map<String, String> header, Map<String, String> entity) {
        showDebug("post START");

        showDebug("URL: " + requestURL);
        HttpResult httpResult = new HttpResult();
        httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_FAILED);
        GZIPInputStream responseGZipDataInputStream = null;
        InputStream in = null;

        HttpClient client = null;

        try {
            // 请求回来的url 可能是半成品
            String url = requestURL;
            if (requestURL.contains("?")) {
                String[] urls = requestURL.split("?");
                url = urls[0];
                if (urls.length > 1) {
                    String param = urls[1];
                    String[] params = param.split("&");
                    for (String par : params) {
                        String[] keyvalue = par.split("=");
                        entity.put(keyvalue[0], keyvalue[1]);
                    }
                }
            }

            HttpPost httpPost = new HttpPost(url);
            addHttpPostHeader(httpPost, header);
            setHttpPostEntity(httpPost, entity);

            client = HttpUtils.getHttpClient();
            HttpResponse response = client.execute(httpPost);

            // showDebug("getting HTTP Status");

            int status = response.getStatusLine().getStatusCode();
            httpResult.setIsConnected(true);
            httpResult.setHttpStatus(status);

            showDebug("HTTP Status: " + status);

            //登陆错误重新登陆~
            if (status == HttpURLConnection.HTTP_UNAUTHORIZED) {
                //  showDebug("HTTP Status: " + "post again");
                entity.remove("access_token");
                LoginApplication.getInstance().conflictlogout();
                return doPost(requestURL, header, entity);
            }

            if (status == HttpURLConnection.HTTP_OK) {

                String result = null;
                httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_SUCCESS);
                if (response.getEntity().getContentEncoding() != null
                        && "gzip".equalsIgnoreCase(response.getEntity()
                        .getContentEncoding().getValue())) {
                    // Decompression input stream.u
                    in = response.getEntity().getContent();

                    responseGZipDataInputStream = new GZIPInputStream(in);

                    byte[] respBuffer = new byte[1024];
                    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                    int count = -1;
                    while ((count = responseGZipDataInputStream.read(
                            respBuffer, 0, 1024)) != -1) {
                        outStream.write(respBuffer, 0, count);
                    }
                    byte[] data = outStream.toByteArray();
                    outStream.close();
                    result = new String(data);
                } else {
                    result = EntityUtils.toString(response.getEntity());
                }
                showDebug("Result: " + result);
                showDebug("OK");
                showDebug("HTTP success");
                httpResult.setData(result);
            } else {
                showDebug("wrong status, failed");
            }
        } catch (IOException e) {
            httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_IO_EXCEPTION);
            String error = StrUtils.notNullStr(e.getMessage());
            httpResult.setErrorMessage(error);
            showDebug("EXC " + error);
        } catch (Exception e) {
            httpResult.setErrorCode(HttpResult.HTTP_RESULT_CODE_EXCEPTION);
            String error = StrUtils.notNullStr(e.getMessage());
            httpResult.setErrorMessage(error);
            showDebug("EXC " + error);
        } finally {
            try {
                if (responseGZipDataInputStream != null) {
                    responseGZipDataInputStream.close();
                }
            } catch (Exception e) {
                showDebug("is exc " + StrUtils.notNullStr(e.getMessage()));
            }

            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                showDebug("is exc " + StrUtils.notNullStr(e.getMessage()));
            }

            if (client != null) {
                client.getConnectionManager().shutdown();
            }
        }
        showDebug("post END");
        return httpResult;

       /*// 建立HTTP Post连线
        HttpPost httpRequest = new HttpPost();
         //Post运作传送变数必须用NameValuePair[]阵列储存
        //传参数 服务端获取的方法为request.getParameter("name")
        List<NameValuePair>  params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("name", "this is post"));

            try {
                //发出HTTP request
                httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
                //取得HTTP response
                HttpResponse  httpResponse=new DefaultHttpClient().execute(httpRequest);

                //若状态码为200 ok
                if(httpResponse.getStatusLine().getStatusCode() == 200){
                    //取出回应字串
                    String strResult = EntityUtils.toString(httpResponse.getEntity());
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        return null;*/
    }

    private static void showDebug(String s) {
        Log.v(LOG_TAG, s);
    }

    private static void addHttpPostHeader(HttpPost httpPost,
                                          Map<String, String> header) {
        if (httpPost == null) {
            return;
        }
        if (header != null && !header.isEmpty()) {

            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpPost.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    private static void addHttpGetHeader(HttpGet httpGet,
                                         Map<String, String> header) {
        if (httpGet == null) {
            return;
        }
        if (header != null && !header.isEmpty()) {

            for (Map.Entry<String, String> entry : header.entrySet()) {
                httpGet.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    private static void setHttpPostEntity(HttpPost httpPost, Map<String, String> entity) throws UnsupportedEncodingException {
        if (httpPost == null) {
            return;
        }
        List<BasicNameValuePair> params = null;
        if (entity != null && !entity.isEmpty()) {

            params = new ArrayList<BasicNameValuePair>();
            for (Map.Entry<String, String> entry : entity.entrySet()) {
                params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
        }
    }

    private static String toHttpGetUrl(String url, Map<String, String> entity) {
        StringBuilder urlSB = new StringBuilder();
        if (entity != null && !entity.isEmpty()) {

            for (Map.Entry<String, String> entry : entity.entrySet()) {
                if (urlSB.length() != 0) {
                    urlSB.append("&");
                }
                urlSB.append(entry.getKey() + "="
                        + Uri.encode(entry.getValue()));
            }
        }
        // 请求回来的url 可能是半成品
        if (url.contains("?")) {
            return url + "&" + urlSB.toString();
        } else {
            return url + "?" + urlSB.toString();
        }
    }

    private static class CustomSSLSocketFactory extends SSLSocketFactory {
        SSLContext mSSLContext = SSLContext.getInstance("TLS");

        public CustomSSLSocketFactory(KeyStore truststore)
                throws NoSuchAlgorithmException, KeyManagementException,
                KeyStoreException, UnrecoverableKeyException {
            super(truststore);

            TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            mSSLContext.init(null, new TrustManager[]{tm}, null);
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port,
                                   boolean autoClose) throws IOException, UnknownHostException {
            return mSSLContext.getSocketFactory().createSocket(socket, host,
                    port, autoClose);
        }

        @Override
        public Socket createSocket() throws IOException {
            return mSSLContext.getSocketFactory().createSocket();
        }
    }
}
